import datetime
import logging

from selenium import webdriver
from selenium.webdriver import Keys, ActionChains
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.wait import WebDriverWait

from utils.log_util import logger


class BasePage:
    def __init__(self, driver):
        self.driver = driver
        self.driver.maximize_window()
        self.driver.implicitly_wait(10)  # 隐示等待
        self.wait = WebDriverWait(driver, 10)  # 显示等待
        self.driver.get("http://meikefresh.5istudy.online/")
        self.action = ActionChains(driver)

    # 基础封装
    # def find_element(self,locator):
    #     logger.info(f"当前定位{locator}")
    #     return self.driver.find_element(*locator)

    def find_element(self, locator, condition="visibility", retry=1):
        logger.info(f"当前定位元素{locator}")
        for time in range(retry + 1):
            try:
                if condition == "visibility":
                    node = self.wait.until(expected_conditions.visibility_of_element_located(locator))
                else:
                    node = self.wait.until(expected_conditions.presence_of_element_located(locator))
                return node
            except Exception as e:
                if time < retry:
                    error_info = f"{locator}元素定位失败，当前正在重试次数{time + 1}"
                    logger.info(error_info)
                else:
                    error_info = f"{locator}元素定位失败，错误信息{e}"
                    logger.error(error_info)
                    raise Exception(error_info)

    def find_elements(self, locator, retry=1):
        logger.info(f"当前定位元素{locator}")
        for time in range(retry + 1):
            try:
                nodes = self.wait.until(lambda x: x.find_elements(*locator))
                return nodes
            except Exception as e:
                if time < retry:
                    error_info = f"{locator}元素定位失败，当前正在重试次数{time + 1}"
                    logger.info(error_info)
                else:
                    error_info = f"{locator}元素定位失败，错误信息{e}"
                    logger.error(error_info)
                    raise Exception(error_info)


    def send_keys(self, locator, content, enter=False):
        node = self.find_element(locator)
        node.clear()
        node.send_keys(content)
        logger.info(f"输入内容为{content}")
        # 调用回车按键
        if enter:
            node.send_keys(Keys.ENTER)
            logger.info("点击回车键")


    def click(self, locator):
        node = self.find_element(locator)
        node.click()
        logger.info("点击按钮")


    def get_url(self, url):
        self.driver.get(url)
        logger.info("打开浏览器")


    def close_driver(self):
        self.driver.close()
        self.driver.quit()
        logger.info("关闭浏览器")


    def refresh(self):
        self.driver.refresh()
        logger.info("刷新浏览器")


    def switch_to_window(self, to_parent_window=False):
        all_windows = self.driver.window_handles
        if to_parent_window:
            self.driver.switch_to.window(all_windows[0])
        else:
            current_window = self.driver.current_window_handle
            for window in all_windows:
                if window != current_window:
                    logger.info(f"切换窗口{window}")
                    self.driver.switch_to.window(window)


    def get_title(self):
        """
        获取浏览器标题
        :return:
        """
        return self.driver.title


    def get_current_url(self):
        return self.driver.current_url


    def get_page_source(self):
        return self.driver.page_source


    def get_text(self, locator):
        ele = self.find_element(locator)
        text = ele.text
        if text == "":
            text = ele.accessible_name
        logger.info(f"元素{locator}的text为{text}")
        return text


    def move_to_element(self, locator):
        ele = self.find_element(locator)
        self.action.move_to_element(ele).perform()
        logger.info(f"鼠标悬浮到元素{locator}上")


    def drag_and_drop(self, locator_start, locator_end):
        start = self.find_element(locator_start)
        end = self.find_element(locator_end)
        self.action.drag_and_drop(start, end).perform()
        logger.info(f"鼠标从{start}拖动到{end}")


    def drag_and_drop_by_offset(self, locator, x, y):
        ele = self.find_element(locator)
        self.action.drag_and_drop_by_offset(ele, x, y).perform()
        logger.info(f"元素{locator}拖动一段距离")


    def select_by_index(self, locator, index):
        ele = self.find_element(locator)
        select = Select(ele)
        select.select_by_index(index)
        logger.info(f"根据下标{index}获取select")


    def select_by_value(self, locator, value):
        ele = self.find_element(locator)
        select = Select(ele)
        select.select_by_value(value)
        logger.info(f"根据value {value}获取select")


    def select_by_visible_text(self, locator, visibletext):
        ele = self.find_element(locator)
        select = Select(ele)
        select.select_by_visible_text(visibletext)
        logger.info(f"根据visible_text {visibletext}获取select")


    def wait_ele_presence(self, locator, center=True):
        """     知识点解析：
                #scrollIntoView:
                        # 如果为true，元素的顶端将和其所在滚动区的可视区域的顶端对齐。
                        # 如果为false，元素的底端将和其所在滚动区的可视区域的底端对齐。
                #scrollIntoViewIfNeeded:
                        #如果为true，则元素将在其所在滚动区的可视区域中居中对其。
                        # 如果为false，则元素将与其所在滚动区的可视区域最近的边缘对齐。 根据可见区域最靠近元素的哪个边缘，
                        # 元素的顶部将与可见区域的顶部边缘对准，或者元素的底部边缘将与可见区域的底部边缘对准。"""

        try:
            start = datetime.datetime.now()
            ele = self.wait.until(expected_conditions.presence_of_element_located(locator))
            end = datetime.datetime.now()
            logger.info("元素{}已存在，等待{}秒".format(locator, (end - start).seconds))
            # 让元素滚动到可视区域
            self.driver.execute_script("arguments[0].scrollIntoViewIfNeeded(arguments[1]);", ele, center)
            return ele
        except Exception as e:
            logger.error(f"元素{locator}找不到，不存在")
            raise


    def execute_js(self, element):
        self.driver.execute_script("arguments[0].click();", element)


    def switch_to_frame(self, index=0, to_parent_frame=False, to_default_frame=False):
        """
                切换到不同的frame框架
                :param index: expect by frame index value or id or name or element
                :param to_parent_frame: 是否切换到上一个frame，默认False
                :param to_default_frame: 是否切换到最上层的frame，默认False
                :return:
                """
        if to_parent_frame:
            self.driver.switch_to.parent_frame()
        elif to_default_frame:
            self.driver.switch_to.default_content()
        else:
            self.driver.switch_to.frame(index)

        logger.info(f"切换frame,to:{index}")


    def popup_window_operation(self, action="yes", send_info="", get_window_info=False):
        """
                弹窗操作
                :param action: 要执行的动作，yes or no
                :param send_info: 在弹窗的文本框内输入信息
                :param get_window_info: 获取弹窗的文本信息
                :return:
                """
        if self.wait.until(expected_conditions.alert_is_present()):
            if send_info:
                self.driver.switch_to.alert.send_keys(send_info)
                logger.info(f"在弹窗上输入信息：{send_info}")
            if get_window_info:
                info = self.driver.switch_to.alert.text
                logger.info(f"获取弹窗上的文本信息：{info}")
                return info
            if action == "yes":
                logger.info("在弹窗上点击确定")
                self.driver.switch_to.alert.accept()
            else:
                logger.info("在弹窗上点击取消")
                self.driver.switch_to.alert.dismiss()


    def page_scroll(self, to_bottom=False, to_distance=(0, 1000)):
        """
               页面滚动，如果没有滚动效果，添加延时（页面需要全部加载完毕才能滚动）
               :param bool to_bottom: 是否直接滚动到当前页面的最底部，默认False
               :param tuple to_distance: 滚动距离，默认是向下滚动1000像素
               :return:
               """
        if to_bottom:
            self.driver.execute_script("window.scrollTo(document.body.scrollHeight)")
        else:
            self.driver.execute_script("window.scrollBy({},{})".format(to_distance[0], to_distance[1]))
        logger.info("页面滚动完毕")


    def back(self):
        self.driver.back()
        logger.info("网页后退")


    def forward(self):
        self.driver.forward()
        logger.info("网页前进")


    def get_element_attribute(self, locator, attributeName, center=True):
        try:
            ele = self.wait_ele_presence(locator, center)
            value = ele.get_attribute(attributeName)
            logger.info("元素{}的属性{}为：{}".format(locator, attributeName, value))
            return value
        except:
            logger.error("元素{}获取属性{}失败".format(locator, attributeName))
            raise
